1 package edu.jiangxin.apktoolbox.android.dumpsys; 2 3 import edu.jiangxin.apktoolbox.android.dumpsys.tojson.IDumpsys2Json; 4 import edu.jiangxin.apktoolbox.swing.extend.EasyPanel; 5 import edu.jiangxin.apktoolbox.swing.extend.filepanel.FilePanel; 6 import edu.jiangxin.apktoolbox.utils.Constants; 7 import org.apache.commons.exec.CommandLine; 8 import org.apache.commons.exec.DefaultExecutor; 9 import org.apache.commons.exec.PumpStreamHandler; 10 import org.apache.commons.io.FileUtils; 11 import org.apache.commons.io.FilenameUtils; 12 import org.apache.commons.lang3.StringUtils; 13 import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; 14 import org.fife.ui.rsyntaxtextarea.SyntaxConstants; 15 import org.fife.ui.rtextarea.RTextScrollPane; 16 17 import javax.swing.*; 18 import java.awt.*; 19 import java.awt.event.ActionEvent; 20 import java.awt.event.ActionListener; 21 import java.io.ByteArrayOutputStream; 22 import java.io.File; 23 import java.io.IOException; 24 import java.io.Serial; 25 import java.text.SimpleDateFormat; 26 import java.util.Date; 27 28 public class DumpsysPanel extends EasyPanel { 29 @Serial 30 private static final long serialVersionUID = 1L; 31 32 private JPanel dumpsysPanel; 33 34 private FilePanel dumpsysTargetDirPanel; 35 36 private JPanel dumpsysOptionPanel; 37 private JPanel dumpsysTypeChoosePanel; 38 39 private JPanel dumpsysOperationPanel; 40 private JButton dumpsysStartButton; 41 42 private JPanel analysisPanel; 43 private FilePanel analysisFilePanel; 44 private RSyntaxTextArea analysisOutputTextArea; 45 private RTextScrollPane analysisOutputScrollPane; 46 47 private JPanel analysisOperationPanel; 48 49 public DumpsysPanel() { 50 super(); 51 } 52 53 @Override 54 public void initUI() { 55 setPreferredSize(new Dimension(700, 400)); 56 57 JTabbedPane tabbedPane = new JTabbedPane(); 58 add(tabbedPane); 59 60 createDumpsysPanel(); 61 tabbedPane.addTab("Dumpsys", null, dumpsysPanel, "Dumpsys"); 62 63 createAnalysisPanel(); 64 tabbedPane.addTab("Analysis", null, analysisPanel, "Analysis"); 65 } 66 67 @Override 68 public void afterPainted() { 69 super.afterPainted(); 70 dumpsysTargetDirPanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysTargetDirPanel"); 71 analysisFilePanel.setPersistentKey(Constants.KEY_PREFIX + "dumpsysAnalysisFilePanel"); 72 } 73 74 private void createDumpsysPanel() { 75 dumpsysPanel = new JPanel(); 76 BoxLayout layout = new BoxLayout(dumpsysPanel, BoxLayout.Y_AXIS); 77 dumpsysPanel.setLayout(layout); 78 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 79 80 createDumpsysTargetDirPanel(); 81 dumpsysPanel.add(dumpsysTargetDirPanel); 82 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 83 84 createDumpsysOptionPanel(); 85 dumpsysPanel.add(dumpsysOptionPanel); 86 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 87 88 createDumpsysOperationPanel(); 89 dumpsysPanel.add(dumpsysOperationPanel); 90 } 91 92 private void createAnalysisPanel() { 93 analysisPanel = new JPanel(); 94 BoxLayout layout = new BoxLayout(analysisPanel, BoxLayout.Y_AXIS); 95 analysisPanel.setLayout(layout); 96 dumpsysPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 97 98 createAnalysisTargetFilePanel(); 99 analysisPanel.add(analysisFilePanel); 100 analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 101 102 createAnalysisOutputPanel(); 103 analysisPanel.add(analysisOutputScrollPane); 104 analysisPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 105 106 createAnalysisOperationPanel(); 107 analysisPanel.add(analysisOperationPanel); 108 } 109 110 private void createAnalysisTargetFilePanel() { 111 analysisFilePanel = new FilePanel("Analysis File"); 112 analysisFilePanel.initialize(); 113 analysisFilePanel.setFileSelectionMode(JFileChooser.FILES_ONLY); 114 } 115 116 private void createAnalysisOutputPanel() { 117 analysisOutputTextArea = new RSyntaxTextArea(); 118 analysisOutputTextArea.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_JSON); 119 analysisOutputTextArea.setCodeFoldingEnabled(true); 120 analysisOutputTextArea.setEditable(false); 121 122 analysisOutputScrollPane = new RTextScrollPane(analysisOutputTextArea); 123 analysisOutputScrollPane.setPreferredSize(new Dimension(400, 250)); 124 } 125 126 private void createAnalysisOperationPanel() { 127 analysisOperationPanel = new JPanel(); 128 BoxLayout boxLayout = new BoxLayout(analysisOperationPanel, BoxLayout.X_AXIS); 129 analysisOperationPanel.setLayout(boxLayout); 130 131 JButton analysisStartButton = new JButton("Start"); 132 analysisStartButton.setEnabled(true); 133 analysisStartButton.addActionListener(new AnalysisStartButtonActionListener()); 134 analysisOperationPanel.add(analysisStartButton); 135 } 136 137 private void createDumpsysTargetDirPanel() { 138 dumpsysTargetDirPanel = new FilePanel("Target Directory"); 139 dumpsysTargetDirPanel.initialize(); 140 dumpsysTargetDirPanel.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); 141 } 142 143 private void createDumpsysOptionPanel() { 144 dumpsysOptionPanel = new JPanel(); 145 BoxLayout boxLayout = new BoxLayout(dumpsysOptionPanel, BoxLayout.Y_AXIS); 146 dumpsysOptionPanel.setLayout(boxLayout); 147 148 JPanel dumpsysCheckboxPanel = new JPanel(); 149 dumpsysCheckboxPanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3)); 150 151 JCheckBox allSelectedCheckBox = new JCheckBox("Select All"); 152 allSelectedCheckBox.setSelected(false); 153 allSelectedCheckBox.addActionListener(e -> { 154 boolean selected = allSelectedCheckBox.isSelected(); 155 if (selected) { 156 Component[] components = dumpsysTypeChoosePanel.getComponents(); 157 for (Component component : components) { 158 if (component instanceof JCheckBox checkBox) { 159 checkBox.setSelected(true); 160 checkBox.setEnabled(false); 161 } 162 } 163 } else { 164 Component[] components = dumpsysTypeChoosePanel.getComponents(); 165 for (Component component : components) { 166 if (component instanceof JCheckBox checkBox) { 167 checkBox.setSelected(false); 168 checkBox.setEnabled(true); 169 } 170 } 171 } 172 173 }); 174 dumpsysCheckboxPanel.add(allSelectedCheckBox); 175 176 dumpsysTypeChoosePanel = new JPanel(); 177 dumpsysTypeChoosePanel.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 3)); 178 dumpsysTypeChoosePanel.setPreferredSize(new Dimension(500, 200)); 179 180 JCheckBox inputCheckBox = new JCheckBox("input"); 181 inputCheckBox.setSelected(true); 182 JCheckBox windowCheckBox = new JCheckBox("window"); 183 dumpsysTypeChoosePanel.add(windowCheckBox); 184 JCheckBox SurfaceFlingerCheckBox = new JCheckBox("SurfaceFlinger"); 185 dumpsysTypeChoosePanel.add(SurfaceFlingerCheckBox); 186 JCheckBox activityCheckBox = new JCheckBox("activity"); 187 dumpsysTypeChoosePanel.add(activityCheckBox); 188 JCheckBox accessibilityCheckBox = new JCheckBox("accessibility"); 189 dumpsysTypeChoosePanel.add(accessibilityCheckBox); 190 JCheckBox packageCheckBox = new JCheckBox("package"); 191 dumpsysTypeChoosePanel.add(packageCheckBox); 192 JCheckBox alarmCheckBox = new JCheckBox("alarm"); 193 dumpsysTypeChoosePanel.add(alarmCheckBox); 194 JCheckBox inputMethodCheckBox = new JCheckBox("input_method"); 195 dumpsysTypeChoosePanel.add(inputMethodCheckBox); 196 JCheckBox sensorServiceCheckBox = new JCheckBox("sensorservice"); 197 dumpsysTypeChoosePanel.add(sensorServiceCheckBox); 198 JCheckBox accountCheckBox = new JCheckBox("account"); 199 dumpsysTypeChoosePanel.add(accountCheckBox); 200 JCheckBox displayCheckBox = new JCheckBox("display"); 201 dumpsysTypeChoosePanel.add(displayCheckBox); 202 JCheckBox powerCheckBox = new JCheckBox("power"); 203 dumpsysTypeChoosePanel.add(powerCheckBox); 204 JCheckBox batteryCheckBox = new JCheckBox("battery"); 205 dumpsysTypeChoosePanel.add(batteryCheckBox); 206 207 dumpsysOptionPanel.add(dumpsysCheckboxPanel); 208 dumpsysOptionPanel.add(new JSeparator(JSeparator.HORIZONTAL)); 209 dumpsysOptionPanel.add(Box.createVerticalStrut(Constants.DEFAULT_Y_BORDER)); 210 dumpsysOptionPanel.add(dumpsysTypeChoosePanel); 211 } 212 213 private void createDumpsysOperationPanel() { 214 dumpsysOperationPanel = new JPanel(); 215 BoxLayout boxLayout = new BoxLayout(dumpsysOperationPanel, BoxLayout.X_AXIS); 216 dumpsysOperationPanel.setLayout(boxLayout); 217 218 dumpsysStartButton = new JButton("Start"); 219 dumpsysStartButton.setEnabled(true); 220 dumpsysStartButton.addActionListener(new DumpsysStartButtonActionListener()); 221 dumpsysOperationPanel.add(dumpsysStartButton); 222 } 223 224 class DumpsysStartButtonActionListener implements ActionListener { 225 @Override 226 public void actionPerformed(ActionEvent e) { 227 dumpsysStartButton.setEnabled(false); 228 File targetDirFile = dumpsysTargetDirPanel.getFile(); 229 if (!targetDirFile.exists()) { 230 JOptionPane.showMessageDialog(getFrame(), "Target directory does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE); 231 return; 232 } 233 234 String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date(System.currentTimeMillis())); 235 File targetDirWithTimestamp = new File(targetDirFile, timeStamp); 236 237 if (!targetDirWithTimestamp.exists()) { 238 boolean success = targetDirWithTimestamp.mkdirs(); 239 if (!success) { 240 JOptionPane.showMessageDialog(getFrame(), "Create target directory failed!", "ERROR", JOptionPane.ERROR_MESSAGE); 241 return; 242 } 243 } 244 245 Component[] components = dumpsysTypeChoosePanel.getComponents(); 246 for (Component component : components) { 247 if (component instanceof JCheckBox checkBox) { 248 String dumpsysType = checkBox.getText(); 249 if (checkBox.isSelected()) { 250 dumpsysAndSaveToFile(dumpsysType, targetDirWithTimestamp); 251 } 252 } 253 } 254 dumpsysStartButton.setEnabled(true); 255 } 256 257 private void dumpsysAndSaveToFile(String dumpsysType, File targetDir) { 258 String command = "adb shell dumpsys " + dumpsysType; 259 CommandLine cmdLine = CommandLine.parse(command); 260 DefaultExecutor executor = DefaultExecutor.builder().get(); 261 try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 262 ByteArrayOutputStream errorStream = new ByteArrayOutputStream();) { 263 PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream, errorStream); 264 executor.setStreamHandler(streamHandler); 265 int exitValue = executor.execute(cmdLine); 266 if (exitValue == 0) { 267 String result = outputStream.toString().trim(); 268 if (StringUtils.isNotEmpty(result)) { 269 File outputFile = new File(targetDir, dumpsysType + ".dumpsys"); 270 FileUtils.writeStringToFile(outputFile, result, "UTF-8"); 271 } 272 } else { 273 String errorResult = errorStream.toString().trim(); 274 if (StringUtils.isNotEmpty(errorResult)) { 275 File errorFile = new File(targetDir, dumpsysType + ".error"); 276 FileUtils.writeStringToFile(errorFile, errorResult, "UTF-8"); 277 } 278 } 279 } catch (IOException ex) { 280 JOptionPane.showMessageDialog(getFrame(), "Execute command failed:\n" + ex.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE); 281 } 282 } 283 } 284 285 class AnalysisStartButtonActionListener implements ActionListener { 286 287 @Override 288 public void actionPerformed(ActionEvent e) { 289 File analysisFile = analysisFilePanel.getFile(); 290 if (!analysisFile.exists()) { 291 JOptionPane.showMessageDialog(getFrame(), "Analysis file does not exist!", "ERROR", JOptionPane.ERROR_MESSAGE); 292 return; 293 } 294 String fileNameWithoutExtension = FilenameUtils.getBaseName(analysisFile.getName()); 295 String className = StringUtils.capitalize(fileNameWithoutExtension) + "2Json"; 296 IDumpsys2Json dumpsys2Json = getDumpsys2JsonInstance(className); 297 if (dumpsys2Json != null) { 298 String jsonResult = dumpsys2Json.dumpsys2Json(analysisFile); 299 analysisOutputTextArea.setText(jsonResult); 300 analysisOutputTextArea.setCaretPosition(0); 301 } 302 } 303 304 private IDumpsys2Json getDumpsys2JsonInstance(String className) { 305 try { 306 String fullClassName = "edu.jiangxin.apktoolbox.android.dumpsys.tojson." + className; 307 Class<?> clazz = Class.forName(fullClassName); 308 if (IDumpsys2Json.class.isAssignableFrom(clazz)) { 309 return (IDumpsys2Json) clazz.getDeclaredConstructor().newInstance(); 310 } else { 311 JOptionPane.showMessageDialog(getFrame(), "Class " + fullClassName + " does not implement IDumpsys2Json!", "ERROR", JOptionPane.ERROR_MESSAGE); 312 return null; 313 } 314 } catch (ClassNotFoundException e) { 315 JOptionPane.showMessageDialog(getFrame(), "Class not found: " + className, "ERROR", JOptionPane.ERROR_MESSAGE); 316 return null; 317 } catch (Exception e) { 318 JOptionPane.showMessageDialog(getFrame(), "Error loading class: " + e.getMessage(), "ERROR", JOptionPane.ERROR_MESSAGE); 319 return null; 320 } 321 } 322 } 323 }